#include "acceptor.hpp"
#include "event_loop.hpp"
#include <error.h>

using namespace event_framework;

bool Acceptor::Listen(int backlog)
{
    ASSERT(backlog > 0);
    // reserve a fd for refusing errno=EMFILE request
    idleFd = open("/dev/null", O_RDONLY | O_CLOEXEC);
    ASSERT(idleFd);
    int err = socket.Bind();
    if (err < 0)
    {
        EVENT_TRACE_ERROR("[Acceptor::Listen] bind failed with err=" << error);
        return false;
    }
    acceptChannel = std::make_shared<Channel>(loop, socket.GetFd());
    ASSERT(acceptChannel != nullptr);
    acceptChannel->ConcernReadableEvent(std::bind(&Acceptor::Accept, this));
    err = socket.Listen(backlog);
    if (err < 0)
    {
        EVENT_TRACE_ERROR("[Acceptor::Listen] listen failed with err=" << error);
        return false;
    }
    return true;
}
void Acceptor::Fini()
{
    if (finished) return;
    finished = true;
    acceptChannel->DisableAll();
    socket.Close();
    close(idleFd);
}
void Acceptor::Accept()
{
    EndPoint ep;
    int cliSocket = socket.Accept(ep);
    if (cliSocket < 0)
    {
        EVENT_TRACE_ERROR("[Acceptor::Accept] fd is exhuast, we will close half-connection socket");
        // maybe exist the problem of "accept()ing when you can’t" when file descriptor is exhuast, 
        // refer to://blog.csdn.net/zhangyifei216/article/details/49789445
        if (errno == EMFILE && idleFd > 0)
        {
            close(idleFd);
            idleFd = accept(acceptChannel->Fd(), NULL, NULL);
            close(idleFd);
            idleFd = open("/dev/null", O_RDONLY | O_CLOEXEC);
        }
        return;
    }
    if (callback)
    {
        Socket socket(ep, cliSocket);
        callback(socket);
    }
    else
    {
        close(cliSocket);
    }
}